package org.ngrinder.demand.controller;

import static org.apache.commons.io.FilenameUtils.getPath;
import static org.ngrinder.common.util.ExceptionUtils.processException;
import static org.ngrinder.common.util.Preconditions.checkNotNull;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.mutable.MutableInt;
import org.ngrinder.common.constant.ControllerConstants;
import org.ngrinder.common.controller.BaseController;
import org.ngrinder.common.controller.RestAPI;
import org.ngrinder.infra.spring.RemainedPath;
import org.ngrinder.model.PerfTest;
import org.ngrinder.model.Role;
import org.ngrinder.model.TestRequirement;
import org.ngrinder.model.User;
import org.ngrinder.perftest.service.PerfTestService;
import org.ngrinder.script.model.FileCategory;
import org.ngrinder.script.model.FileEntry;
import org.ngrinder.script.service.FileEntryService;
import org.python.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefaults;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

@Controller
@RequestMapping("/demand")
public class DemandController extends BaseController implements ControllerConstants {

	@Autowired
	private TestRequirementService testRequirementService;
	@Autowired
	private FileEntryService fileEntryService;
	@Autowired
	private PerfTestService perfTestService;
	
	@RequestMapping({"/list", "/", ""})
	public String list(User user, ModelMap model,
				@PageableDefaults Pageable pageable,
			   HttpServletResponse response, HttpServletRequest request) {
		Page<TestRequirement> tests = testRequirementService.getPagedAll(user, pageable);
		
		model.addAttribute("testListPage", tests);
		model.addAttribute("page", pageable);
		
		return "demand/list";
	}
	
	@RequestMapping({"/new"})
	public String detail(User user, ModelMap model,
			   HttpServletResponse response, HttpServletRequest request) {
		
		
		return "demand/detail";
	}
	
	@RequestMapping(value = "/new", method = RequestMethod.POST)
	public String saveOne(User user, TestRequirement testRequirement,
			ModelMap model) throws URISyntaxException {
		
		boolean isAdd = false;
		if (testRequirement == null) {
			testRequirement = new TestRequirement();
			testRequirement.init();
		}
		testRequirement.setCreatedUser(user);
		testRequirement.setLastModifiedUser(user);
		
		if (testRequirement.getId() == null || testRequirement.getId() <= 0) {
			isAdd = true;
		}
		
		
		testRequirementService.save(user, testRequirement);
		
		if (testRequirement.getTestInfoList().trim() != "") {
			List<TestInfo> testInfoList = new Gson().fromJson(testRequirement.getTestInfoList(), new TypeToken<List<TestInfo>>() {}.getType());
			
//			int idx = 0;
			for (TestInfo ti : testInfoList) {
				URI uri = new URI(ti.getUrl());
				String targetHosts = uri.getHost();
				FileEntry fileEntry = new FileEntry();
				String content = "";
//				String pathUrl = uri.getPath() != null ? "_" + uri.getPath().replace("/", "").trim() : "";
				
				String path = "/"+ testRequirement.getProjectName().trim().replace(" ", "_") + "_" + testRequirement.getId() +"/"+ ti.name +".py";
				
				try {
					Map<String, Object> values = new HashMap<String, Object>();
					values.put("userName", user.getUserName());
					values.put("name", targetHosts);
					values.put("url", ti.getUrl());
					values.put("type", ti.getType());
					values.put("checkStr", ti.getHeightAttr().getCheckStr());
					
					String params = "";
					String headers = "";
					String urls = "result = request1."+ ti.getType() +"(\""+ ti.getUrl() +"\")";
					for (Params p : ti.getHeightAttr().getParams()) {
						if (ti.heightAttr.getParamsType().equals("json")) {
							params += "\"" + p.getParamsKey() + "\"" + ":" + "\"" + p.getParamsValue() + "\",";
						} else {
							params += "NVPair(\""+ p.getParamsKey() +"\",\""+ p.getParamsValue() +"\"),";
						}
					}
					if (!params.equals("")) {
						if (ti.getHeightAttr().getParamsType().equals("json")) {
							params = "submitdata = '{"+ params.substring(0, params.length() - 1) +"}'";
						} else {
							params = "submitdata = ["+ params.substring(0, params.length() - 1) +"]";
						}
						
						urls = "result = request1."+ ti.getType() +"(\""+ ti.getUrl() +"\", submitdata)";
					}
					
					values.put("params", params);
					
					for (Headers h : ti.getHeightAttr().getHeaders()) {
						headers += "NVPair(\""+ h.getHeadersKey() +"\",\""+ h.getHeadersValue() +"\"),";
					}
					if (!headers.equals("")) {
						headers = "headers = [" + headers.substring(0, headers.length() - 1) + "]";
						
						urls = "result = request1."+ ti.getType() +"(\""+ ti.getUrl() +"\", None, headers)";
					}
					values.put("headers", headers);
					
					if (!headers.equals("") && !params.equals("")) {
						urls = "result = request1."+ ti.getType() +"(\""+ ti.getUrl() +"\", submitdata, headers)";
					}
					
					values.put("urls", urls);
					
					
					Configuration freemarkerConfig = new Configuration();
					ClassPathResource cpr = new ClassPathResource("script_template");
					freemarkerConfig.setDirectoryForTemplateLoading(cpr.getFile());
					freemarkerConfig.setObjectWrapper(new DefaultObjectWrapper());
					Template template = freemarkerConfig.getTemplate("basic_template_py2.ftl");
					StringWriter writer = new StringWriter();
					template.process(values, writer);
					content = writer.toString();
				} catch (Exception e) {
					throw processException("Error while fetching the script template.", e);
				}
				
				fileEntry.setContent(content);
				fileEntry.setDescription(ti.getSubmitInfo() == null ? "" : ti.getSubmitInfo());
				fileEntry.setPath(path);
				
				if (fileEntry.getFileType().getFileCategory() == FileCategory.SCRIPT) {
					Map<String, String> map = Maps.newHashMap();
					map.put("validated", "0");
					map.put("targetHosts", StringUtils.trim(targetHosts));
					fileEntry.setProperties(map);
				}
				fileEntryService.save(user, fileEntry);	
				
				String basePath = getPath(fileEntry.getPath());
				fileEntryService.addFolder(user, basePath, "lib", getMessages("script.commit.libFolder"));
				fileEntryService.addFolder(user, basePath, "resources", getMessages("script.commit.resourceFolder"));
			}
		}
		
		
		model.clear();
		
		return "redirect:/demand/list";
	}
	
	@RequestMapping("/{id}")
	public String getOne(User user, @PathVariable("id") Long id, ModelMap model) {
		TestRequirement test = null;
		if (id != null) {
			test = getOneWithPermissionCheck(user, id);
		}

		if (test == null) {
			test = new TestRequirement(user);
			test.init();
		}

		model.addAttribute("test", test);
		// Retrieve the agent count map based on create user, if the test is
		// created by the others.
		user = test.getCreatedUser() != null ? test.getCreatedUser() : user;

		
		return "demand/detail";
	}
	
	@RequestMapping({"/report/{id}"}) 
	public String report(User user, ModelMap model, @PathVariable("id") Long id,
			   HttpServletResponse response, HttpServletRequest request) {
		
		TestRequirement test = null;
		if (id != null) {
			test = getOneWithPermissionCheck(user, id);
		}
		model.addAttribute("test", test);
		
		Pageable pageable = new Pageable() {
			
			@Override
			public Sort getSort() {
				// TODO Auto-generated method stub
				return null;
			}
			
			@Override
			public int getPageSize() {
				// TODO Auto-generated method stub
				return 1000;
			}
			
			@Override
			public int getPageNumber() {
				// TODO Auto-generated method stub
				return 0;
			}
			
			@Override
			public int getOffset() {
				// TODO Auto-generated method stub
				return 0;
			}
		};
		Page<PerfTest> tests = perfTestService.getPagedAll(user, "demandId=" + id, "", "", pageable);
		model.addAttribute("testListPage", tests);
		
		return "demand/report";
	}
	
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public HttpEntity<String> upload(User user,  HttpServletRequest request,
	                     @RequestParam("uploadFile") MultipartFile file, ModelMap model) {
		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
    	String date = df.format(new Date());
    	
		if (!file.isEmpty()) {  
            try {  
                // 文件保存路径  
            	String filePath = request.getSession().getServletContext().getRealPath("/") + "upload\\"  
                        + user.getId() + "\\" + date + "\\";
                File dir = new File(filePath);
                if  (!dir.exists()  && !dir.isDirectory()) {
                	dir.mkdirs();
                }
                
                // 转存文件  
                file.transferTo(new File(filePath + file.getOriginalFilename()));  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            
            Map<String, String> result = new HashMap<String, String>();
            result.put("path", "upload/" +  user.getId() + "/" + date + "/" + file.getOriginalFilename());
            
            return toJsonHttpEntity(result);
        } else {
        	return errorJsonHttpEntity();
        }  
	}
	
	@RestAPI
	@RequestMapping(value = "/api", method = RequestMethod.DELETE)
	public HttpEntity<String> delete(User user, @RequestParam(value = "ids", defaultValue = "") String ids) {
		for (String idStr : StringUtils.split(ids, ",")) {
			testRequirementService.delete(user, NumberUtils.toLong(idStr, 0));
		}
		return successJsonHttpEntity();
	}
	
	@RestAPI
	@RequestMapping(value = "/api/{id}", method = RequestMethod.DELETE)
	public HttpEntity<String> delete(User user, @PathVariable("id") Long id) {
		TestRequirement testRequirement = getOneWithPermissionCheck(user, id);
		checkNotNull(testRequirement, "no perftest for %s exits", id);
		testRequirementService.delete(user, id);
		return successJsonHttpEntity();
	}
	
	private TestRequirement getOneWithPermissionCheck(User user, Long id) {
		TestRequirement testRequirement = testRequirementService.getOne(id);
		if (user.getRole().equals(Role.ADMIN) || user.getRole().equals(Role.SUPER_USER)) {
			return testRequirement;
		}
		if (testRequirement != null && !user.equals(testRequirement.getCreatedUser())) {
			throw processException("User " + user.getUserId() + " has no right on PerfTest " + id);
		}
		return testRequirement;
	}

	
	class TestInfo {
		private String name;
		private String url;
		private String type;
		private int readonly = 0;
		private String submitInfo;
		private HeightAttr heightAttr;
		
		
		
		public String getSubmitInfo() {
			return submitInfo;
		}
		public void setSubmitInfo(String submitInfo) {
			this.submitInfo = submitInfo;
		}
		public HeightAttr getHeightAttr() {
			return heightAttr;
		}
		public void setHeightAttr(HeightAttr heightAttr) {
			this.heightAttr = heightAttr;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getUrl() {
			return url;
		}
		public void setUrl(String url) {
			this.url = url;
		}
		public String getType() {
			return type;
		}
		public void setType(String type) {
			this.type = type;
		}
		public int getReadonly() {
			return readonly;
		}
		public void setReadonly(int readonly) {
			this.readonly = readonly;
		}
		public TestInfo(String name, String url, String type, int readonly) {
			super();
			this.name = name;
			this.url = url;
			this.type = type;
			this.readonly = readonly;
		}
		
		@Override
		public String toString() {
			return "TestInfo [name=" + name + ", url=" + url + ", type=" + type
					+ ", readonly=" + readonly + ", heightAttr=" + heightAttr
					+ "]";
		}
		
		
	}
	
	class HeightAttr {
		private String checkStr;
		private String paramsType;
		private List<Headers> headers;
		private List<Params> params;
		public String getCheckStr() {
			return checkStr;
		}
		public void setCheckStr(String checkStr) {
			this.checkStr = checkStr;
		}
		public String getParamsType() {
			return paramsType;
		}
		public void setParamsType(String paramsType) {
			this.paramsType = paramsType;
		}
		public List<Headers> getHeaders() {
			return headers;
		}
		public void setHeaders(List<Headers> headers) {
			this.headers = headers;
		}
		public List<Params> getParams() {
			return params;
		}
		public void setParams(List<Params> params) {
			this.params = params;
		}
		@Override
		public String toString() {
			return "HeightAttr [checkStr=" + checkStr + ", paramsType="
					+ paramsType + ", headers=" + headers + ", params="
					+ params + "]";
		}
		
		
	}
	
	class Headers {
		private String headersKey;
		private String headersValue;
		
		public String getHeadersKey() {
			return headersKey;
		}
		public void setHeadersKey(String headersKey) {
			this.headersKey = headersKey;
		}
		public String getHeadersValue() {
			return headersValue;
		}
		public void setHeadersValue(String headersValue) {
			this.headersValue = headersValue;
		}
		@Override
		public String toString() {
			return "Headers [headersKey=" + headersKey + ", headersValue="
					+ headersValue + "]";
		}
		
	}
	
	class Params {
		private String paramsKey;
		private String paramsValue;
		public String getParamsKey() {
			return paramsKey;
		}
		public void setParamsKey(String paramsKey) {
			this.paramsKey = paramsKey;
		}
		public String getParamsValue() {
			return paramsValue;
		}
		public void setParamsValue(String paramsValue) {
			this.paramsValue = paramsValue;
		}
		@Override
		public String toString() {
			return "Params [paramsKey=" + paramsKey + ", paramsValue="
					+ paramsValue + "]";
		}
		
		
	}
	
}
